package com.cruise.game.security.filter;

import com.cruise.game.common.constants.SecurityConstants;
import com.cruise.game.common.enums.RoleEnum;
import com.cruise.game.common.utils.JwtUtils;
import com.cruise.game.mapper.UserMapper;
import com.cruise.game.model.entity.User;
import com.cruise.game.service.common.AuthService;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import io.jsonwebtoken.Claims;

/**
 * JWT认证过滤器
 *
 * @author cruise
 * @date 2025-02-28
 */
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private JwtUtils jwtUtils;

    @Resource
    private UserMapper userMapper;

    // 添加白名单路径配置，不需要验证的路径
    private static final String[] AUTH_WHITELIST = {
            "/api/auth/wx-login",
            "/api/auth/refresh-token",
            "/api/public/**",
            "/swagger-ui/**",
            "/v3/api-docs/**"
    };

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            // 检查是否是白名单路径
            String requestURI = request.getRequestURI();
            boolean isWhitelistPath = false;
            for (String path : AUTH_WHITELIST) {
                if (path.endsWith("/**") && requestURI.startsWith(path.substring(0, path.length() - 3))) {
                    isWhitelistPath = true;
                    break;
                } else if (requestURI.startsWith(path)) {
                    isWhitelistPath = true;
                    break;
                }
            }

            if (isWhitelistPath) {
                log.debug("请求路径在白名单中，跳过JWT认证: {}", requestURI);
                filterChain.doFilter(request, response);
                return;
            }

            // 获取JWT令牌
            String token = jwtUtils.getTokenFromRequest(request);

            // 验证JWT令牌
            if (StringUtils.isNotBlank(token)) {
                try {
                    // 从JWT中获取用户ID和角色
                    Long userId = jwtUtils.getUserIdFromToken(token);
                    String role = jwtUtils.getUserRoleFromToken(token);

                    log.debug("JWT解析 - 用户ID: {}, 原始角色: {}", userId, role);

                    // 设置角色权限
                    List<SimpleGrantedAuthority> authorities = new ArrayList<>();

                    // 确保角色格式正确
                    String roleCode = role.toLowerCase(); // 将角色转换为小写（与JwtUtils保持一致）
                    // 如果角色不是以ROLE_开头，则添加前缀
                    if (!roleCode.startsWith(SecurityConstants.ROLE_PREFIX.toLowerCase())) {
                        roleCode = SecurityConstants.ROLE_PREFIX + roleCode;
                    }

                    log.debug("最终使用的角色Code: {}", roleCode);
                    // 确保角色名称全部大写，这是Spring Security的约定
                    authorities.add(new SimpleGrantedAuthority(roleCode.toUpperCase()));

                    // 将用户信息存入SecurityContext
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userId,
                            null, authorities);
                    SecurityContextHolder.getContext().setAuthentication(authentication);

                    log.debug("认证信息设置成功，权限: {}", authorities);

                } catch (ExpiredJwtException e) {
                    log.warn("JWT令牌已过期: {}", e.getMessage());
                    // 尝试从过期令牌中获取用户ID和角色，用于提示用户刷新令牌
                    try {
                        Claims claims = e.getClaims();
                        if (claims != null && claims.containsKey("userId")) {
                            Long userId = Long.valueOf(claims.get("userId").toString());
                            String role = claims.get("role").toString();
                            log.debug("从过期令牌中提取信息 - 用户ID: {}, 角色: {}", userId, role);

                            // 添加自定义响应头表明令牌已过期，前端可以据此刷新令牌
                            response.setHeader("X-Token-Expired", "true");
                            response.setHeader("X-User-Id", userId.toString());

                            // 实现自动刷新令牌的逻辑 - 仅在非刷新令牌接口时执行
                            if (!requestURI.contains("/api/auth/refresh-token")) {
                                // 从数据库获取用户信息
                                User user = userMapper.selectById(userId);
                                if (user != null) {
                                    // 生成新的令牌
                                    String newToken = jwtUtils.generateToken(userId, role);
                                    // 将新令牌放入响应头
                                    response.setHeader("Authorization", "Bearer " + newToken);
                                    response.setHeader("Access-Control-Expose-Headers", "Authorization");

                                    // 同时设置认证信息，让当前请求能够正常处理
                                    List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                                    String roleCode = role.toLowerCase();
                                    if (!roleCode.startsWith(SecurityConstants.ROLE_PREFIX.toLowerCase())) {
                                        roleCode = SecurityConstants.ROLE_PREFIX + roleCode;
                                    }
                                    // 确保角色名称全部大写，这是Spring Security的约定
                                    authorities.add(new SimpleGrantedAuthority(roleCode.toUpperCase()));

                                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                                            userId, null, authorities);
                                    SecurityContextHolder.getContext().setAuthentication(authentication);
                                    log.info("JWT令牌已自动刷新，用户ID: {}", userId);
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error("无法从过期令牌中提取信息", ex);
                    }
                } catch (UnsupportedJwtException e) {
                    log.warn("不支持的JWT令牌: {}", e.getMessage());
                } catch (MalformedJwtException e) {
                    log.warn("JWT令牌格式错误: {}", e.getMessage());
                } catch (SignatureException e) {
                    log.warn("JWT签名验证失败: {}", e.getMessage());
                } catch (IllegalArgumentException e) {
                    log.warn("JWT令牌参数异常: {}", e.getMessage());
                }
            } else {
                log.debug("请求未包含有效的JWT令牌, URI: {}", request.getRequestURI());
            }
        } catch (Exception e) {
            log.error("无法设置用户认证信息", e);
        }

        filterChain.doFilter(request, response);
    }
}
